/* t_hashkv.c : test dgnsc hashkv
 * Copyright (C) 2011 drangon <drangon.zhou@gmail.com>
 * 2011-09
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <dgnsc/hashkv.h>

#include "gtest.h"

#include <stdio.h>
// #include <assert.h>
#include <string.h>
#include <stdlib.h>

struct hkv_item_st
{
	void * key;
	void * value;

	unsigned int hash;
	struct hkv_item_st * prev;
	struct hkv_item_st * next;
};

struct hashkv_st
{
	int item_num;
	int prime_idx;
	struct hkv_item_st ** hash_box; // hash array, size is s_prime[prime_idx] 
	struct hkv_item_st hash_list;

	dgn_hashkv_hash_func_t hash_fn;
	dgn_hashkv_cmp_func_t cmp_fn;
	dgn_hashkv_freek_func_t freek_fn;
	dgn_hashkv_freev_func_t freev_fn;
};

static const unsigned int s_prime[] = {
	13, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
	49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
	12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
	805306457, 1610612741
};


static int validate_hashkv( dgn_hashkv_t hkv )
{
	struct hashkv_st * h = (struct hashkv_st *)hkv;
	struct hkv_item_st * it = NULL;

	// validate list
	int i;
	for( i = 0, it = h->hash_list.next; i < h->item_num && it != &h->hash_list; i++, it = it->next ) {
		EXPECT_TRUE( it->next->prev == it );
		EXPECT_TRUE( it->prev->next == it );
	}
	EXPECT_TRUE( i == h->item_num );
	EXPECT_TRUE( it == &h->hash_list );
	
	// validate hash
	int num = 0;
	for( i = 0; i < s_prime[h->prime_idx]; ++i ) {
		it = h->hash_box[i];
		if( it == NULL )
			continue;
		EXPECT_TRUE( (it->hash % s_prime[h->prime_idx]) == i );
		num += 1;
		for( it = it->next; it != &h->hash_list; it = it->next ) {
			if( (it->hash % s_prime[h->prime_idx]) != i )
				break;
			num += 1;
		}
	}
	EXPECT_TRUE( num == h->item_num );
	return 0;
}

static int dump_hashkv( dgn_hashkv_t hkv )
{
	struct hashkv_st * h = (struct hashkv_st *)hkv;
	struct hkv_item_st * it = NULL;

	// dump list
	printf( "hash num %d, prime_idx %d ( %d )\n", h->item_num, h->prime_idx, s_prime[h->prime_idx] );
	int i;
	for( i = 0, it = h->hash_list.next; i < h->item_num && it != &h->hash_list; i++, it = it->next ) {
		printf( "%d : key [%s], hash %u ( %u )\n", i, (char *)it->key, it->hash % s_prime[h->prime_idx], it->hash );
	}
	
	// dump hash
	int num = 0;
	for( i = 0; i < s_prime[h->prime_idx]; ++i ) {
		it = h->hash_box[i];
		if( it == NULL ) {
			printf( "%4d : (null)\n", i );
			continue;
		}

		printf( "%4d : key [%s], hash %u ( %u )\n", i, (char *)it->key, it->hash % s_prime[h->prime_idx], it->hash );
		num += 1;
		for( it = it->next; it != &h->hash_list; it = it->next ) {
			if( (it->hash % s_prime[h->prime_idx]) != i )
				break;
			printf( "     : key [%s], hash %u ( %u )\n", (char *)it->key, it->hash % s_prime[h->prime_idx], it->hash );
			num += 1;
		}
	}
	return 0;
}

TEST( hashkv, hashkv )
{
	dgn_hashkv_t hkv;
	char * key;
	char * val;
	int i;

	hkv = dgn_hashkv_init_default();
	for( i = 0; i < 3000; i++ ) {
		key = (char *)malloc( 32 );
		val = (char *)malloc( 256 );
		sprintf( key, "%d", i );
		sprintf( val, "%d", i );
		// printf( "%d, key %p, val %p\n", i, key, val );
		if( i % 1000 == 0 )
			printf( "add i %d\n", i );
		dgn_hashkv_add( hkv, key, val );
	//	if( i == 10 )
	//		dump_hashkv( hkv );
		validate_hashkv( hkv );
	}

	key = (char *)malloc( 32 );
	val = (char *)malloc( 256 );
	sprintf( key, "8" );
	sprintf( val, "88" );

	char * tmp1 = (char *)1;
	char * tmp2 = (char *)2;

	dgn_hashkv_add_ex( hkv, key, val, (void **)&tmp1, (void **)&tmp2 );
	printf( "add_ex : key %p, val %p, tmp1 %p, tmp2 %p\n", key, val, tmp1, tmp2 );
	dgn_hashkv_set( hkv, tmp1, tmp2, (void **)&tmp2 );
	printf( "set : tmp1 %p, tmp2 %p\n", tmp1, tmp2 );
	free( tmp1 );
	free( tmp2 );

	key = (char *)malloc( 32 );
	sprintf( key, "9" );
	dgn_hashkv_del_ex( hkv, key, (void **)&tmp1, (void **)&tmp2 );
	printf( "add_ex : key %p, tmp1 %p, tmp2 %p\n", key, tmp1, tmp2 );
	free( tmp1 );
	free( tmp2 );
	free( key );

	key = (char *)malloc( 32 );
	sprintf( key, "8" );
	val = (char *)dgn_hashkv_get( hkv, key );
	printf( "set : key %p, val %p\n", key, val );
	sprintf( key, "8a" );
	val = (char *)dgn_hashkv_get( hkv, key );
	printf( "set : key %p, val %p\n", key, val );
	free( key );

	key = (char *)malloc( 32 );
	for( i = 0; i < 180; i++ ) {
		sprintf( key, "%d", i * 8 );
		// printf( "%d, key %p, val %p\n", i, key, val );
		if( i % 10 == 0 )
			printf( "del i %d\n", i );
		dgn_hashkv_del( hkv, key );
	//	if( i == 10 )
	//		dump_hashkv( hkv );
		validate_hashkv( hkv );
	}
	free( key );

	dgn_hashkv_fini( hkv );

}

